package com.jbp.service;

import com.alibaba.fastjson.JSON;
import com.jbp.exception.RRException;
import com.jbp.model.Gift;
import com.jbp.model.GiftLot;
import com.jbp.model.LuckPoolRoom;
import com.jbp.model.LuckPoolUser;
import com.jbp.redis.RedisConfigService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.util.*;

/**
 * 呱呱 50块羊毛奖池 1：10
 */
@Service
public class LuckyUtil42Wool {
    @Autowired
    private RedisConfigService redisConfigService;

    @Autowired
    private PropertyService propertyService;

    @Autowired
    private UserService userService;

    //每次抽消耗奖金币
    public static final int price = 500;
    @Autowired
    private LuckPoolUserService luckPoolUserService;
    @Autowired
    private LuckPoolRoomService luckPoolRoomService;

    @Autowired
    private LuckyUtil luckyUtil;
    /**
     * patch 补礼物  0表示不补
     * */
    public List<Gift> luck(Integer userId, int times, List<GiftLot> list, int patch, Integer roomId) {

        //系统总爆率
        double winRate;
        double winRateA = Double.parseDouble(propertyService.getV("winRateA"));
        //奖池礼物数量
        int poolSize = Integer.parseInt(propertyService.getV("pool_size2"));
        int minPoolSize = Integer.parseInt(propertyService.getV("min_pool_size2"));
        int maxGift = Integer.parseInt(propertyService.getV("maxGift"));
        double jcBeishu = Double.parseDouble(propertyService.getV("pool_times"));

        String keyUpay = "haoya:luck:"+price+":upay:"+userId;
        String keyUincmoe = "haoya:luck:"+price+":uincome:"+userId;
        String keyUTotalPay = "haoya:luck:"+price+":utotalpay:"+userId;
        String keyUTotalIncome = "haoya:luck:"+price+":utotalincome:"+userId;
        String keyUmap = "haoya:luck:"+price+":umap:"+userId;

        int pay = redisConfigService.getInt(keyUpay);
        int income = redisConfigService.getInt(keyUincmoe);
        int totalPay = redisConfigService.getInt(keyUTotalPay);
        int totalIncome = redisConfigService.getInt(keyUTotalIncome);

        double userBaseRate = luckyUtil.getBaseRate(roomId, userId, totalPay);

        //判断奖池
        String poolName;
        int type;
        double basePoolRate = 1;
        int biggest = 200000;

        poolName = "A";
        type = 1;
        winRate = winRateA;

        LuckPoolRoom luckPoolRoom = luckPoolRoomService.getByRoomId(roomId);
        LuckPoolUser luckPoolUser = luckPoolUserService.getByUserId(userId);
        String poolPrex = "";
        if (luckPoolUser!=null) {
            poolPrex = "u:"+userId+":";
            winRate *= luckPoolUser.getRate().doubleValue() / 1.17d;
        } else if (luckPoolRoom!=null) {
            poolPrex = "r:"+roomId+":";
            winRate *= luckPoolRoom.getRate().doubleValue() / 1.17d;
        }

        double patchRate = winRate<1.17 ? 1.17-winRate : 0;

        Node nodeTree = getNodeTree(type);

        String keyAllincome= "haoya:luck:"+price+":allincome:"+poolPrex+poolName;
        String keyAllpay = "haoya:luck:"+price+":allpay:"+poolPrex+poolName;
        String keyPool = "haoya:luck:"+price+":pool:"+poolPrex+poolName;
        String keyGiftCount = "haoya:luck:"+price+":giftcount:"+poolPrex+poolName;
        String keyPatchPool = "haoya:luck:patchpool";
        String keyPoolMap = "haoya:luck:"+price+":pollmap:"+poolPrex+type;

        int allIncome = redisConfigService.getInt(keyAllincome);
        int allPay = redisConfigService.getInt(keyAllpay);
        int pool = redisConfigService.getInt(keyPool);
        int giftCount = redisConfigService.getInt(keyGiftCount);
        int patchPool = redisConfigService.getInt(keyPatchPool);

        //用户积分树
        String ujson = (String) redisConfigService.get(keyUmap);
        Map<String, Integer> userNMap = (Map<String, Integer>) JSON.parse(ujson);
        if (userNMap==null) {
            userNMap = new LinkedHashMap<>();
        }
        //奖池积分树
        String pjson = (String) redisConfigService.get(keyPoolMap);
        Map<String, Integer> allNMap = (Map<String, Integer>) JSON.parse(pjson);
        if (allNMap==null) {
            allNMap = new LinkedHashMap<>();
        }

        //生成奖池
        if (giftCount<=minPoolSize) {
            giftCount += poolSize;
            pool += poolSize * winRate * price;
            if (pool>giftCount*1.19*price) {
                pool = (int) (giftCount*1.19*price);
            }
            System.out.println("生成奖池："+pool);
            patchPool += poolSize * patchRate * price;
        }

        //当前抽奖十连或百连
        int currPay = 0, currIncome=0;
        List<Gift> result = new ArrayList<>();
        for (int i = 0; i < times; i++) {
            giftCount --;
            pay += price;
            totalPay += price;
            allPay += price;
            currPay += price;

            //用户最大奖限制
            biggest = Math.min(biggest, (int) (pool/jcBeishu));
            int min = 1;
            int luck;
            if (patch==0) {
                biggest = Math.min(biggest, maxGift);
                biggest = Math.max(biggest, 4444);
                biggest = Math.min(biggest, 131400);

                int lose = pay - income;
                if (totalPay <= 500000 && totalPay > 300000 && lose>0) {
                    min = lose;
                }

                // 十连保底
                if (times==10) {
                    int currMin = 5000;//十连B奖池保底
                    if (type!=2) { //其他奖池
                        currMin = 3000;
                    }
                    if (currPay==10000 && currIncome<currMin) {
                        min = currMin - currIncome;
                        biggest = 6666;
                    }
                } else if (times==100) {
                    int currMin = 80000;
                    if (currPay==100000 && currIncome<currMin) {
                        min = currMin - currIncome;
//                        biggest = 66666;
                    }
                }
                min = Math.min(min, biggest);

                double poolRate = LuckyUtil.getPoolRate(pool, giftCount, winRate, price);
                poolRate = Math.min(poolRate, 1.2d);
                luck = LuckyUtil.nodeLuck(nodeTree, poolRate, min, biggest, userNMap, allNMap, basePoolRate*userBaseRate);
            } else {
                luck = patch;
//                LuckyUtil.patch(nodeTree,luck, userNMap, allNMap);
            }

            //将奖品转化为礼物
            Gift gift = null;
            for (GiftLot giftLot : list) {
                if (giftLot.getGift().getCoin() == luck) {
                    gift = giftLot.getGift();
                    break;
                }
            }

            if (gift==null) {
                System.out.println("抽奖异常");
                throw new RRException("网络错误");
            }

            //新号补不计入爆率
            income += gift.getCoin();
            totalIncome += gift.getCoin();
            allIncome += gift.getCoin();
            currIncome += gift.getCoin();
            if (patch==0) {
                pool -= gift.getCoin();
            }
            result.add(gift);
        }

        pool = Math.min(pool, (int) (giftCount*1.17*price+50000));

        redisConfigService.set(keyAllincome, allIncome);
        redisConfigService.set(keyAllpay, allPay);
        redisConfigService.set(keyPool, pool);
        redisConfigService.set(keyPatchPool, patchPool);
        redisConfigService.set(keyGiftCount, giftCount);
        redisConfigService.set(keyUpay, pay);
        redisConfigService.set(keyUincmoe, income);
        redisConfigService.set(keyUTotalPay, totalPay);
        redisConfigService.set(keyUTotalIncome, totalIncome);
        redisConfigService.set(keyUmap, JSON.toJSONString(userNMap));
        redisConfigService.set(keyPoolMap, JSON.toJSONString(allNMap));
        return result;
    }


    Pool poolA = new Pool();
    Pool poolB = new Pool();
    Pool poolC = new Pool();
    Pool poolD = new Pool();
    @SuppressWarnings("AliControlFlowStatementWithoutBraces")
    void luckTest(UserLuck userLuck, Map <Integer, Integer> out) {
        //总爆率
        double winRate;

        Pool pl;
        int type;
        int biggest = 200000 ;
        pl = poolA;
        type =1;
        winRate = 1.17;
        Node nodeTree = getNodeTree(type);

        //生成奖池
        if (pl.giftCount<500) {
            pl.giftCount += pl.giftSize;
            pl.pool += pl.giftSize * winRate * price;
        }

        pl.giftCount --;
        userLuck.pay += price;
        userLuck.totalPay += price;
        userLuck.currPay += price;
        pl.allPay += price;


        //用户最大奖限制

        int min = 1;

        biggest = Math.min(biggest, pl.pool/5);
        biggest = Math.min(biggest, 131400);
        biggest = Math.max(biggest, 4444);

        int lose = (int) (userLuck.pay-userLuck.income);
        if (userLuck.pay <= 500000 && userLuck.pay > 300000 && lose>0) {
            min = lose;
        }

        if (userLuck.totalPay==10000 && userLuck.totalIncome<3000) {
            min = 3000-userLuck.totalIncome;
            biggest = 6666;
        }

        int currMin = 80000;
        if (userLuck.totalPay==100000 && userLuck.totalIncome<currMin) {
            min = currMin - userLuck.totalIncome;
            biggest = 66666;
        }

        min = Math.min(min, biggest);

        double poolRate = LuckyUtil.getPoolRate(pl.pool, pl.giftCount, winRate, price);
        Map<String, Integer> nMap = getUserNMap(userLuck.id);
        Map<String, Integer> allMap = getPoolNMap(type);
        int luck = LuckyUtil.nodeLuck(nodeTree, poolRate, min, biggest, nMap, allMap, 1);

        userLuck.income += luck;
        userLuck.totalIncome += luck;
        userLuck.currIncome += luck;
        pl.allIncome += luck;
        pl.pool -= luck;

        if (out.containsKey(luck)) {
            out.put(luck, out.get(luck)+1);
        } else {
            out.put(luck, 1);
        }
    }

    /**
     * A奖池 投入1w钻以内
     */
    private static Map <Integer, Integer> getGiftCoinfigA() {
        Map<Integer, Integer> map = new LinkedHashMap<>();
        map.put(88, 1100);
        map.put(188, 650);
        map.put(288, 520);
        map.put(488, 500);
        map.put(888, 270);
        map.put(1888, 130);
        map.put(3388, 60);
        map.put(5188, 32);
        map.put(8888, 16);
        map.put(11888, 9);
        map.put(18888, 4);
        map.put(28888, 2);
        map.put(33888, 1);
        return map;
    }

    /**
     1. 存储用户每个礼物对应的N值 默认为1 未抽中+1 抽中置为1
     2. prd算法 P(实际概率)=C(基础爆率)*N(抽奖次数)
     */
    private static Map<Integer, Map<Integer, Integer>> userNMap = new HashMap<>();

    /**
     * 获取用户真实爆率表
     * 实际礼物爆率为 礼物基础爆率*N
     * */
    private static Map<Integer, Integer> getGiftConfig(Integer userId) {
        Map<Integer, Integer> nMap = userNMap.get(userId);
        Map<Integer, Integer> giftCoinfig = getGiftCoinfigA();
        Map<Integer, Integer> result = new LinkedHashMap<> ();
        for (Integer price : giftCoinfig.keySet()) {
            result.put(price, giftCoinfig.get(price));
            if (nMap!=null && nMap.containsKey(price)) {
                result.put(price, (int) (giftCoinfig.get(price)*nMap.get(price)));
            }
        }
        return result;
    }

    private static Node getNodeTree(Map<String, Integer> map){
        Node g1 = new Node("g1", new Node(18888), new Node(28888), map);
        Node f1 = new Node("f1", new Node(8888), new Node(11888), map);
        Node f2 = new Node("f2", g1, new Node(33888), map);
        Node e1 = new Node("e1", new Node(3388), new Node(5188), map);
        Node e2 = new Node("e2", f1, f2, map);
        Node d1 = new Node("d1", new Node(888), new Node(1888), map);
        Node d2 = new Node("d2", e1, e2, map);
        Node c1 = new Node("c1", new Node(288), new Node(488), map);
        Node c2 = new Node("c2", d1, d2, map);
        Node b1 = new Node("b1", new Node(88), new Node(188), map);
        Node b2 = new Node("b2", c1, c2, map);
        return new Node("a", b1, b2, map);
    }

    private static Map <String, Integer> getCMap1() {
        Map<String, Integer> map = new LinkedHashMap<>();
        map.put("a", 2665);
        map.put("b1", 1748);
        map.put("b2", 1483);
        map.put("c1", 2888);
        map.put("c2", 757);
        map.put("d1", 1360);
        map.put("d2", 893);
        map.put("e1", 1555);
        map.put("e2", 653);
        map.put("f1", 1638);
        map.put("f2", 291);
        map.put("g1", 1424);
        return map;
    }

    static Node node1;

    private static Node getNodeTree(int type){
        if (node1==null) {
            node1 = getNodeTree(getCMap1());
        }
        return node1;
    }

    private static Map<Integer, Map<String, Integer>> userNMap1 = new HashMap<>();
    private static Map<Integer, Map<String, Integer>> poolNMap = new HashMap<>();

    /**奖池总积分树*/
    static Map<String, Integer> getPoolNMap(int type) {
        Map<String, Integer> nMap = poolNMap.get(type);
        if (nMap==null) {
            nMap = new LinkedHashMap<>();
            poolNMap.put(type, nMap);
        }
        return nMap;
    }

    /**用户积分树*/
    static Map<String, Integer> getUserNMap(int id) {
        Map<String, Integer> nMap = userNMap1.get(id);
        if (nMap==null) {
            nMap = new LinkedHashMap<>();
            userNMap1.put(id, nMap);
        }
        return nMap;
    }


    public static void main(String[] args) {
        LuckyUtil.printConfig(getGiftCoinfigA(), price);
        System.out.println("--------------");
        LuckyUtil.calPercent(getNodeTree(getCMap1()), getGiftCoinfigA());

        LuckyUtil42Wool luckyUtil = new LuckyUtil42Wool();
        Map <Integer, Integer> result = new LinkedHashMap<>();
        List<UserLuck> userLucks = new ArrayList<>();
        for (int i = 0; i < 10000; i++) {
            UserLuck user = new UserLuck();
            user.id=i;
            while (true) {
                luckyUtil.luckTest(user, result);
                if (user.totalPay>=10000 || (user.totalIncome*0.85d-user.totalPay>0)) {
                    double rate  = BigDecimal.valueOf(user.totalIncome).divide(BigDecimal.valueOf(user.totalPay), 4, BigDecimal.ROUND_HALF_UP).doubleValue();
//                    System.out.println(i+": 投入："+user.totalPay+" 产出:"+user.totalIncome +"  爆率:"+rate+ " 实际盈利："+(int)(user.totalIncome*0.85d-user.totalPay));
                    break;
                }
            }
            userLucks.add(user);
        }
        LuckyUtil.printConfig(result, price);

        LuckyUtil.tjUser(userLucks);

        System.out.println("------------系统爆率------------");
        System.out.println("A 系统投入:"+luckyUtil.poolA.allPay+" 产出:"+luckyUtil.poolA.allIncome+" 总爆率:"+ ((double)luckyUtil.poolA.allIncome/luckyUtil.poolA.allPay)+" 奖池剩余："+luckyUtil.poolA.giftCount+" 剩余爆率:"+((double)luckyUtil.poolA.pool/price/luckyUtil.poolA.giftCount));
        System.out.println("B 系统投入:"+luckyUtil.poolB.allPay+" 产出:"+luckyUtil.poolB.allIncome+" 总爆率:"+ ((double)luckyUtil.poolB.allIncome/luckyUtil.poolB.allPay)+" 奖池剩余："+luckyUtil.poolB.giftCount+" 剩余爆率:"+((double)luckyUtil.poolB.pool/price/luckyUtil.poolB.giftCount));
        System.out.println("C 系统投入:"+luckyUtil.poolC.allPay+" 产出:"+luckyUtil.poolC.allIncome+" 总爆率:"+ ((double)luckyUtil.poolC.allIncome/luckyUtil.poolC.allPay)+" 奖池剩余："+luckyUtil.poolC.giftCount+" 剩余爆率:"+((double)luckyUtil.poolC.pool/price/luckyUtil.poolC.giftCount));
        System.out.println("D 系统投入:"+luckyUtil.poolD.allPay+" 产出:"+luckyUtil.poolD.allIncome+" 总爆率:"+ ((double)luckyUtil.poolD.allIncome/luckyUtil.poolD.allPay)+" 奖池剩余："+luckyUtil.poolD.giftCount+" 剩余爆率:"+((double)luckyUtil.poolD.pool/price/luckyUtil.poolD.giftCount));
        System.out.println("总 系统投入:"+(luckyUtil.poolA.allPay+luckyUtil.poolB.allPay+luckyUtil.poolC.allPay+luckyUtil.poolD.allPay)+
                " 产出:"+(luckyUtil.poolA.allIncome+luckyUtil.poolB.allIncome+luckyUtil.poolC.allIncome+luckyUtil.poolD.allIncome)+
                " 总爆率:"+ ((double)(luckyUtil.poolA.allIncome+luckyUtil.poolB.allIncome+luckyUtil.poolC.allIncome+luckyUtil.poolD.allIncome)/(luckyUtil.poolA.allPay+luckyUtil.poolB.allPay+luckyUtil.poolC.allPay+luckyUtil.poolD.allPay)));
    }
}

